/* Copyright (c) 2007-2024 by Kithara Software GmbH. All rights reserved. */

//##############################################################################################################
//
// File:         NetworkUdpReceive_dll.cpp
//
// Used Modules: Network Module
//
// Description:  Sample kernel DLL for simple implementation of a UDP receiver
//
// Creator:      r.gro 2007-12-11
//
//##############################################################################################################

   /*=====================================================================*\
   |                    *** DISCLAIMER OF WARRANTY ***                     |
   |                                                                       |
   |       THIS  CODE AND INFORMATION IS PROVIDED "AS IS"  WITHOUT         |
   |       A  WARRANTY OF  ANY KIND, EITHER  EXPRESSED OR  IMPLIED,        |
   |       INCLUDING  BUT NOT LIMITED TO THE IMPLIED WARRANTIES OF         |
   |       MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE.        |
   \*=====================================================================*/

   //##############################################################################################################
   //
   // Purpose:
   //
   // This example shows how to receive UDP datagrams.
   //
   // First we open the network adapter and create a socket.
   // A receive callback is installed at kernel level, to put incoming data into a message pipe and set an event.
   // A receive thread of the application, waiting for that event, gets the data from the pipe and displays it.
   //
   // This sample is designed to be the counterpart to the sample NetworkUdpSend.
   //
   // PLEASE SPECIFY YOUR IP-ADDRESS, SUBNET-MASK AND GATEWAY-IP-ADDRESS IN THE KS_makeIPv4() CALLS AS WELL AS
   // THE PORT TO USE IN THE APPLICATION PROGRAM BEFORE RUNNING THIS EXAMPLE!
   //
   //##############################################################################################################


   //--------------------------------------------------------------------------------------------------------------
   // To share the definition of data structures between the main program and the kernel DLL, we use a common
   // header file.
   //--------------------------------------------------------------------------------------------------------------

#include "NetworkUdp.h"


//--------------------------------------------------------------------------------------------------------------
// Shared memory is used to share data between the DLL in kernel space and the application in user space.
//--------------------------------------------------------------------------------------------------------------

SharedData* _pSys = NULL;


//--------------------------------------------------------------------------------------------------------------
// Address structure for the data receive callback to receive source information about incoming packets.
//--------------------------------------------------------------------------------------------------------------

KSSocketAddr _remoteAddr;

//--------------------------------------------------------------------------------------------------------------
// Target information address structure for the sendBufferContent function
//--------------------------------------------------------------------------------------------------------------

KSSocketAddr _targetAddr;

//--------------------------------------------------------------------------------------------------------------
// Declaration of the callback function that gets defined at the end of this file.
//--------------------------------------------------------------------------------------------------------------

KSError __stdcall socketCallBack(void* /*pArgs*/, void* /*pContext*/);


//--------------------------------------------------------------------------------------------------------------
// This is the initialization function.
// It gets called AFTER loading the kernel and gets the handle of the shared memory passed as argument.
//
// Attention! Please note, that all functions should be declared as 'extern "C"'!
// Otherwise their names could not be found by the loader.
// To show the names, it is necessary to export them by giving the '__declspec(dllexport)'.
//--------------------------------------------------------------------------------------------------------------

extern "C" KSError __declspec(dllexport) __stdcall _initFunction(void* pArgs, void* /*pContext*/) {

    KSError ksError;


    //------------------------------------------------------------------------------------------------------------
    // The pointer to the shared memory gets passed as the 'pArgs' parameter by KS_execKernelFunctionEx().
    //------------------------------------------------------------------------------------------------------------

    _pSys = (SharedData*)pArgs;


    //------------------------------------------------------------------------------------------------------------
    // Create a pipe for incoming data. This will be a message pipe, because the items put into the pipe will
    // have different lengths (depending on the amount of characters entered before 'endline').
    //------------------------------------------------------------------------------------------------------------

    ksError = KS_createPipe(
        &_pSys->hPipe,                            // Address to write the new pipe handle to
        "NetworkUdpReceivePipe",                  // Name of the pipe
        1,                                        // Item size, here bytes
        bufferSize * 2,                           // Item count
        KS_INVALID_HANDLE,                        // Object to signal (unused)
        KSF_MESSAGE_PIPE);                        // Flags, here create a message pipe, not a data pipe
    if (ksError != KS_OK)
        return ksError;


    //------------------------------------------------------------------------------------------------------------
    // Create the receive event.
    //------------------------------------------------------------------------------------------------------------

    ksError = KS_createEvent(
        &_pSys->hReceiveEvent,                    // Address to write the new event handle to
        "NetworkUdpReceiveEvent",                 // Name of the event
        KSF_NO_FLAGS);                            // Flags, here none
    if (ksError != KS_OK)
        return ksError;


    //------------------------------------------------------------------------------------------------------------
    // For signalization we use a callback with KSF_DIRECT_EXEC.
    //------------------------------------------------------------------------------------------------------------

    ksError = KS_createCallBack(
        &_pSys->hSocketCallBack,                  // Address to write the new callback handle to
        socketCallBack,                           // Callback function
        NULL,                                     // Callback parameter (unused)
        KSF_DIRECT_EXEC,                          // Flags, here execution on kernel level
        0);                                       // Priority (unused on kernel level)
    if (ksError != KS_OK)
        return ksError;


    //------------------------------------------------------------------------------------------------------------
    // Now the network adapter will be opened.
    // The name is a part of the device name of the adapter.
    // 'DEV_1229' would work, if there is just one Intel8255x in the system.
    //------------------------------------------------------------------------------------------------------------

    ksError = KS_openNetworkAdapter(
        &_pSys->hAdapter,                         // Address to write the new handle of the adapter to
        _pSys->pDeviceName,                       // Device name of the adapter
        NULL,                                     // Adapter config (optional)
        KSF_NO_FLAGS);                            // Flags, here none
    if (ksError != KS_OK)
        return ksError;


    //------------------------------------------------------------------------------------------------------------
    // Now the network will be configured to use the IP address, subnet and gateway that got specified in the
    // application in userspace.
    //------------------------------------------------------------------------------------------------------------

    ksError = KS_execNetworkCommand(
        _pSys->hAdapter,                          // Adapter handle
        KS_NETWORK_SET_IP_CONFIG,                 // Command
        &_pSys->ipConfig,                         // Address of KSIPConfig structure
        KSF_NO_FLAGS);                            // Flags, here none
    if (ksError != KS_OK)
        return ksError;


    //------------------------------------------------------------------------------------------------------------
    // Now we create a UDP client socket on the port specified by SharedData.listeningPort.
    //------------------------------------------------------------------------------------------------------------

    KSSocketAddr socketAddr = { 0 };
    socketAddr.family = KS_FAMILY_IPV4;
    socketAddr.port = KS_htons(_pSys->listeningPort);
    *socketAddr.address
        = _pSys->ipConfig.localAddress;

    ksError = KS_openSocket(
        &_pSys->hSocket,                          // Address to write the new socket handle to
        _pSys->hAdapter,                          // Adapter handle
        &socketAddr,                              // Address of KSSocketAddr structure
        KS_PROTOCOL_UDP,                          // Socket protocol
        KSF_NO_FLAGS);                            // Flags, here none
    if (ksError != KS_OK)
        return ksError;

    //------------------------------------------------------------------------------------------------------------
    // Set the maximum transfer unit (MTU) for the socket.
    //------------------------------------------------------------------------------------------------------------

    int mtu = 1024;
    ksError = KS_execSocketCommand(
        _pSys->hSocket,                           // Socket handle
        KS_SOCKET_SET_MTU,                        // Id identifying the command to execute
        &mtu,                                     // Address of value for the command to exceute
        KSF_NO_FLAGS);                            // Flags (unused)
    if (ksError != KS_OK)
        return ksError;
    //------------------------------------------------------------------------------------------------------------
    // Install the socket handler for receive event on the UDP socket.
    //------------------------------------------------------------------------------------------------------------

    ksError = KS_installSocketHandler(
        _pSys->hSocket,                           // Socket handle
        KS_SOCKET_RECV,                           // Event code
        _pSys->hSocketCallBack,                   // Handle of the callback to signal
        KSF_NO_FLAGS);                            // Flags (unused)
    if (ksError != KS_OK)
        return ksError;

    //------------------------------------------------------------------------------------------------------------
// Fill the target information address structure for the sendBufferContent function.
//------------------------------------------------------------------------------------------------------------

    _targetAddr.family = KS_FAMILY_IPV4;
    _targetAddr.port = KS_htons(_pSys->listeningPort);
    *_targetAddr.address = _pSys->serverIp;


    return KS_OK;
}


//--------------------------------------------------------------------------------------------------------------
// This is the cleanup function that removes the socket handler, the socket, closes the network adapter,
// removes the callback, the signaling event and the message pipe.
//--------------------------------------------------------------------------------------------------------------

extern "C" KSError __declspec(dllexport) __stdcall _exitFunction(void* /*pArgs*/, void* /*pContext*/) {
    if (_pSys == NULL)                                    // Shared memory not mapped!
        return KSERROR_FUNCTION_NOT_AVAILABLE;              // _initFunction not called?

    KSError ksError;


    //------------------------------------------------------------------------------------------------------------
    // Uninstall the socket handler.
    //------------------------------------------------------------------------------------------------------------

    ksError = KS_installSocketHandler(
        _pSys->hSocket,                           // Socket handle
        KS_SOCKET_RECV,                           // Event code
        KS_INVALID_HANDLE,                        // Reset to invalid callback handle
        KSF_NO_FLAGS);                            // Flags (unused)
    if (ksError != KS_OK)
        return ksError;


    //------------------------------------------------------------------------------------------------------------
    // Close the socket.
    //------------------------------------------------------------------------------------------------------------

    ksError = KS_closeSocket(
        _pSys->hSocket);                          // Socket handle
    if (ksError != KS_OK)
        return ksError;


    //------------------------------------------------------------------------------------------------------------
    // Close the network adapter.
    //------------------------------------------------------------------------------------------------------------

    ksError = KS_closeNetwork(
        _pSys->hAdapter,                          // Adapter handle
        KSF_NO_FLAGS);                            // Flags (unused)
    if (ksError != KS_OK)
        return ksError;


    //------------------------------------------------------------------------------------------------------------
    // Remove callback.
    //------------------------------------------------------------------------------------------------------------

    ksError = KS_removeCallBack(
        _pSys->hSocketCallBack);                  // Callback handle
    if (ksError != KS_OK)
        return ksError;


    //------------------------------------------------------------------------------------------------------------
    // Close the event.
    //------------------------------------------------------------------------------------------------------------

    ksError = KS_closeEvent(
        _pSys->hReceiveEvent);                    // Event handle
    if (ksError != KS_OK)
        return ksError;


    //------------------------------------------------------------------------------------------------------------
    // Remove the message pipe.
    //------------------------------------------------------------------------------------------------------------

    ksError = KS_removePipe(
        _pSys->hPipe);                            // Pipe handle
    if (ksError != KS_OK)
        return ksError;

    return KS_OK;
}


//--------------------------------------------------------------------------------------------------------------
// _sendBufferContent() will get called from the application in userspace and will transmit the content of the
// SharedData.pSendBuffer through the socket to the connected target.
//--------------------------------------------------------------------------------------------------------------

extern "C" __declspec(dllexport) KSError __stdcall _sendBufferContent(void* /*pArgs*/, void* /*pContext*/) {
    KSError ksError;


    if (_pSys->sendLength != 0) {
        ksError = KS_sendToSocket(
            _pSys->hSocket,                         // Socket handle
            &_targetAddr,                           // Address of KSSocketAddr structure for target
            _pSys->pSendBuffer,                     // Address of buffer containing the data to send
            _pSys->sendLength,                      // Length of data to send
            NULL,                                   // Returned length of data sent (not used)
            KSF_NO_FLAGS);                          // Flags (unused)
        if (ksError != KS_OK)
            return ksError;

        ksError = KS_sendToSocket(
            _pSys->hSocket,                         // Socket handle
            &_targetAddr,                           // Address of KSSocketAddr structure for target
            "\r\n",                                 // Send new line after the actual message
            2,                                      // Length of data to send
            NULL,                                   // Returned length of data sent (not used)
            KSF_NO_FLAGS);                          // Flags (unused)
        if (ksError != KS_OK)
            return ksError;
    }

    return KS_OK;
}


//--------------------------------------------------------------------------------------------------------------
// This is the user defined callback function.
// It is called each time a UDP datagram is received and will put the received data in the pipe + raise the
// receive event to signal the receiving thread of the application that new data in the pipe is available.
//--------------------------------------------------------------------------------------------------------------

KSError __stdcall socketCallBack(void* /*pArgs*/, void* /*pContext*/) {
    KSError ksError;
    int length;
    byte pReadBuffer[bufferSize];

    do {


        //----------------------------------------------------------------------------------------------------------
        // Here we take the received UDP datagram.
        //----------------------------------------------------------------------------------------------------------

        ksError = KS_recvFromSocket(
            _pSys->hSocket,                         // Socket handle
            &_targetAddr,                           // Address of KSSocketAddr structure
            pReadBuffer,                            // Data buffer
            bufferSize,                             // Maximum data length
            &length,                                // Received data length
            KSF_NO_FLAGS);                          // Flags, here none
        if (ksError && KSERROR_CODE(ksError) != KSERROR_NO_DATA_AVAILABLE)
            return ksError;

        if (ksError == KS_OK) {


            //--------------------------------------------------------------------------------------------------------
            // Now we put the data into the pipe to transfer it to the application.
            //--------------------------------------------------------------------------------------------------------

            ksError = KS_putPipe(
                _pSys->hPipe,                         // Pipe handle
                pReadBuffer,                          // Buffer with received data to put into pipe
                length,                               // Message length (in bytes)
                NULL,                                 // Returned data length put (unused)
                KSF_NO_FLAGS);                        // Flags (unused)


            //--------------------------------------------------------------------------------------------------------
            // Signal the receiving thread of the application to get the data from the pipe.
            //--------------------------------------------------------------------------------------------------------

            KS_setEvent(
                _pSys->hReceiveEvent);                          // Handle of signaling event
        }
    } while (ksError == KS_OK);

    return KS_OK;
}


//--------------------------------------------------------------------------------------------------------------
// At last we need an implementation of the DllMain function, which will NOT be executed when the DLL is loaded.
//
// For any initializations, define a special init function and either pass its name as parameter when calling
// KS_loadKernel(), or if you'll need the KSHandle of the loaded kernel for later function calls from the loaded
// DLL (like in this example) don't specify an init function to be executed when loading the kernel, but call it
// explicitly on your own AFTER loading the kernel-dll and pass parameters as needed and shown in this example.
//--------------------------------------------------------------------------------------------------------------

#define WIN32_LEAN_AND_MEAN
#pragma pack(push, 8)
#include <windows.h>
#pragma pack(pop)

BOOL WINAPI DllMain(HINSTANCE hInstDll, DWORD reason, LPVOID pReserved) {
    return TRUE;
}
